#include "httpresponse.h"

const std::unordered_map<std::string, std::string> HttpResponse::SUFFIX_TYPE = {
    {".html", "text/html"},
    {".xml", "text/xml"},
    {".xhtml", "application/xhtml+xml"},
    {".txt", "text/plain"},
    {".rtf", "application/rtf"},
    {".pdf", "application/pdf"},
    {".word", "application/nsword"},
    {".png", "image/png"},
    {".gif", "image/gif"},
    {".jpg", "image/jpeg"},
    {".jpeg", "image/jpeg"},
    {".au", "audio/basic"},
    {".mpeg", "video/mpeg"},
    {".mpg", "video/mpeg"},
    {".avi", "video/x-msvideo"},
    {".gz", "application/x-gzip"},
    {".tar", "application/x-tar"},
    {".css", "text/css "},
    {".js", "text/javascript "},
};

const std::unordered_map<int, std::string> HttpResponse::CODE_STATUS = {
    {200, "OK"},
    {400, "Bad Request"},
    {403, "Forbidden"},
    {404, "Not Found"},
};

const std::unordered_map<int, std::string> HttpResponse::CODE_PATH = {
    {400, "/400.html"},
    {403, "/403.html"},
    {404, "/404.html"},
};

HttpResponse::HttpResponse() : code_(-1), isKeepAlive_(false), path_(""), srcDir_(""), mmFile_(nullptr)
{
    mmFileStat_ = {0};
}

HttpResponse::~HttpResponse()
{
    UnmapFile();
}

void HttpResponse::Init(const std::string &srcDir, std::string &path, bool isKeepAlive = false, int code = -1)
{
    assert(srcDir != "");
    if (mmFile_)
    {
        UnmapFile();
    }
    srcDir_ = srcDir;
    path_ = path;
    isKeepAlive_ = isKeepAlive;
    code_ = code;
}

void HttpResponse::MakeResponse(Buffer &buff)
{
    if (stat((srcDir_ + path_).data(), &mmFileStat_) < 0 || S_ISDIR(mmFileStat_.st_mode))
    {
        code_ = 404;
    }
    else if (!(mmFileStat_.st_mode & S_IROTH))
    {
        code_ = 403;
    }
    else if (code_ == -1)
    {
        code_ = 200;
    }
    ErrorHtml_();
    AddStateLine_(buff);
    AddHeader_(buff);
    AddContent_(buff);
}

void HttpResponse::ErrorHtml_()
{
    if (CODE_PATH.count(code_) == 1)
    {
        path_ = CODE_PATH.find(code_)->second;
        stat((srcDir_ + path_).data(), &mmFileStat_);
    }
}

void HttpResponse::AddStateLine_(Buffer &buff)
{
    std::string statu;
    if (CODE_STATUS.count(code_) == 0)
        code_ = 400;
    statu = CODE_STATUS.find(code_)->second;
    buff.Append("HTTP/1.1 " + std::to_string(code_) + " " + statu + "\r\n");
}

void HttpResponse::AddHeader_(Buffer &buff)
{
    buff.Append("Connection: ");
    if (isKeepAlive_)
    {
        buff.Append("keep-alive\r\n");
        buff.Append("keep-alive max=6, timeout=120\r\n");
    }
    else
    {
        buff.Append("close");
    }
    buff.Append("Content-Type: " + GetFileType_() + "\r\n");
}

std::string HttpResponse::GetFileType_()
{
    if (path_ == "")
    {
        LOG_ERROR("path error");
        exit(1);
    }
    std::string::size_type idx = path_.find(".");
    if (idx == std::string::npos)
        return "text/plain"; // 纯文本内容
    std::string suffix = path_.substr(idx);
    if (SUFFIX_TYPE.count(suffix) == 1)
    {
        return SUFFIX_TYPE.find(suffix)->second;
    }
    return "text/plain";
}

void HttpResponse::AddContent_(Buffer &buff)
{
    int fd = open((srcDir_ + path_).data(), O_RDONLY);
    if (fd < 0)
    {
        ErrorContent(buff, "file NotFound!");
        return;
    }
    int *mmfile = static_cast<int *>(mmap(0, mmFileStat_.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
    if (*mmfile == -1)
    {
        ErrorContent(buff, "file NotFound!");
        return;
    }
    mmFile_ = (char *)mmfile;
    close(fd);
    buff.Append("Content-Length: " + std::to_string(mmFileStat_.st_size) + "\r\n\r\n");
}

void HttpResponse::ErrorContent(Buffer &buff, std::string message)
{
    std::string body;
    std::string statu;
    body += "<html><title>Error</title>";
    body += "<body bgcolor=\"ffffff\">";
    if (CODE_STATUS.count(code_))
        statu = CODE_STATUS.find(code_)->second;
    else
        statu = "Bad Request";
    body += std::to_string(code_) + " : " + statu + "\n";
    body += "<p>" + message + "</p>";
    body += "<hr><em>TinyWebServer</em></body></html>";

    buff.Append("Content-length: " + std::to_string(body.size()) + "\r\n\r\n");
    buff.Append(body);
}


void HttpResponse::UnmapFile()
{
    if(mmFile_)
    {
        munmap(mmFile_,mmFileStat_.st_size);
        mmFile_ = nullptr;
    }
}

char* HttpResponse::File() {
    return mmFile_;
}

size_t HttpResponse::FileLen() const {
    return mmFileStat_.st_size;
}